﻿using System;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;

namespace JML.Core.DI
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using Castle.Windsor;
    using Castle.Windsor.Configuration.Interpreters;

    public static class IoC
    {
        private static IWindsorContainer _localInstanceContainer;
        private static readonly object _globalDataContainerKey = new object();

        public static void Initialize()
        {
            Initialize(new WindsorContainer(new XmlInterpreter("Windsor.config")));
        }
        public static void Initialize(string configFilePath)
        {
            Initialize(new WindsorContainer(new XmlInterpreter(configFilePath)));
        }
        public static void Initialize(IWindsorContainer container)
        {
            _LocalInstanceContainer = container;
        }

        public static object Resolve(Type serviceType)
        {
            return Container.Resolve(serviceType);
        }
        public static object Resolve(string serviceName)
        {
            return Container.Resolve(serviceName);
        }
        public static object Resolve(Type serviceType, string serviceName)
        {
            return Container.Resolve(serviceName, serviceType);
        }

        /// <summary>
        /// Tries to resolve the component, but return null
        /// instead of throwing if it is not there.
        /// Useful for optional dependencies.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T TryResolve<T>()
        {
            return TryResolve<T>(default(T));
        }
        /// <summary>
        /// Tries to resolve the compoennt, but return the default 
        /// value if could not find it, instead of throwing.
        /// Useful for optional dependencies.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static T TryResolve<T>(T defaultValue)
        {
            if (Container.Kernel.HasComponent(typeof(T)) == false) return defaultValue;

            return Container.Resolve<T>();
        }

        public static T Resolve<T>()
        {
            return Container.Resolve<T>();
        }
        public static T Resolve<T>(string name)
        {
            return Container.Resolve<T>(name);
        }

        public static bool HasImplementation<T>()
        {
            return Container.Kernel.HasComponent(typeof(T));
        }
        public static bool HasImplementation(string namedComponent)
        {
            return Container.Kernel.HasComponent(namedComponent);
        }

        public static void AddComponent<Implementation>()
        {
            Container.AddComponent<Implementation>();
        }
        public static void AddImplementation<Interface, Implementation>()
        {
            Container.AddComponent(typeof(Implementation).FullName, typeof(Interface), typeof(Implementation));
        }
        public static void AddImplementation<Interface>(object instance)
        {
            Container.Kernel.AddComponentInstance(instance.GetType().FullName, typeof(Interface), instance);
        }

        public static IWindsorContainer Container
        {
            get
            {
                IWindsorContainer result = _GlobalsDataContainer ?? _LocalInstanceContainer;

                if (result == null) throw new InvalidOperationException("The container has not been initialized!");

                return result;
            }
        }

        public static bool IsInitialized
        {
            get { return _LocalInstanceContainer != null; }
        }

        /// <summary>
        /// This allows you to override the global container locally
        /// Useful for scenarios where you are replacing the global container
        /// but needs to do some initializing that relies on it.
        /// </summary>
        public static IDisposable Use(string containerFromConfig)
        {
            return Use(new WindsorContainer(new XmlInterpreter(containerFromConfig)));
        }
        public static IDisposable Use(IWindsorContainer container)
        {
            _GlobalsDataContainerStack.Push(container);

            return new DisposableAction(delegate
            {
                Reset(container);
            });
        }

        public static void Reset()
        {
            IWindsorContainer container = _GlobalsDataContainer ?? _LocalInstanceContainer;

            Reset(container);
        }
        public static void Reset(IWindsorContainer containerToReset)
        {
            if (containerToReset == null) return;

            if (ReferenceEquals(_GlobalsDataContainer, containerToReset))
            {
                _GlobalsDataContainerStack.Pop();

                if (_GlobalsDataContainerStack.Count == 0)
                {
                    Globals.Data[_globalDataContainerKey] = null;
                    containerToReset.Dispose();
                }

                return;
            }

            if (ReferenceEquals(_LocalInstanceContainer, containerToReset))
            {
                _LocalInstanceContainer = null;
                containerToReset.Dispose();
            }
        }



        private static IWindsorContainer _GlobalsDataContainer
        {
            get
            {
                if (_GlobalsDataContainerStack.Count == 0) return null;

                return _GlobalsDataContainerStack.Peek();
            }
        }
        private static Stack<IWindsorContainer> _GlobalsDataContainerStack
        {
            get
            {
                Stack<IWindsorContainer> stack = Globals.Data[_globalDataContainerKey] as Stack<IWindsorContainer>;

                if (stack == null) Globals.Data[_globalDataContainerKey] = stack = new Stack<IWindsorContainer>();


                return stack;
            }
        }

        internal static IWindsorContainer _LocalInstanceContainer
        {
            get
            {
                return _localInstanceContainer;
            }
            set
            {
                _localInstanceContainer = value;
            }
        }
    }


    //public static class IoC
    //{
    //    private static IWindsorContainer _container =
    //        new WindsorContainer(new XmlInterpreter("Windsor.config"));

    //    public static T Resolve<T>()
    //    {
    //        return _container.Resolve<T>();
    //    }

    //    public static object Resolve(Type service)
    //    {
    //        return _container.Resolve(service);
    //    }
    //}
}